package com.cdn.clock.aspect;

import com.cdn.clock.anno.CLock;
import com.cdn.clock.component.LockComponent;
import com.cdn.clock.exception.CLockException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.concurrent.TimeUnit;

/**
 * @author 蔡定努
 * 2024/06/01 23:21
 */
@Aspect
public class CLockAspect {

    @Autowired
    private ApplicationContext applicationContext;
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    @Around("@annotation(com.cdn.clock.anno.CLock)")
    public Object point(ProceedingJoinPoint point) throws Throwable {
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        CLock cLock = method.getAnnotation(CLock.class);
        String key = getKey(method, cLock, point);
        long leaseTime = cLock.leaseTime();
        TimeUnit timeUnit = cLock.timeUnit();
        LockComponent lockComponent = applicationContext.getBean(LockComponent.class);
        boolean lock = lockComponent.lock(key, cLock.waitTime(), leaseTime, timeUnit);
        Object proceed;
        try {
            if (lock) {
                proceed = point.proceed();
            } else {
                throw new CLockException("Failed to acquire locks:" + key);
            }
        } finally {
            if (lock) {
                lockComponent.unLock(key);
            }
        }
        return proceed;
    }

    /**
     * @author 蔡定努
     */
    private String getKey( Method method,CLock cLock,ProceedingJoinPoint point) {
        StringBuilder builder = new StringBuilder();
        Object[] args = point.getArgs();
        String[] value = cLock.key();
        EvaluationContext context = getEvaluationContext(method, args);
        for (String s : value) {
            if (s.contains("#")) {
                Expression expression = EXPRESSION_PARSER.parseExpression(s);
                Object value1 = expression.getValue(context);
                if (null != value1) {
                    builder.append(value1);
                }
            }else {
                builder.append(s);
            }
        }
        String methodName = method.getName();
        Object target = point.getTarget();
        String className = target.getClass().getName();
        return className + "." + methodName + "." + builder;
    }


    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();


    /**
     * 获取方法上的参数
     *
     * @param method 方法
     * @param args   变量
     * @return {SimpleEvaluationContext}
     */
    private MethodBasedEvaluationContext getEvaluationContext(Method method, Object[] args) {
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(null, method, args, parameterNameDiscoverer);
        context.setBeanResolver(new BeanFactoryResolver(applicationContext));
        return context;
    }
}
